package auction.securechannel;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.apache.log4j.Logger;


public class AESChannel extends ChannelDecorator{
	public static Logger log = Logger.getLogger(AESChannel.class);

	private Cipher crypt ;

	public AESChannel(Channel decoratedChannel) throws NoSuchAlgorithmException, NoSuchPaddingException {
		super(decoratedChannel);
		Security.addProvider(new BouncyCastleProvider());
		crypt = Cipher.getInstance("AES/CTR/NoPadding"); 
	}

	@Override
	public void send(byte[]  message)  {
		try {

			message = encrypt(message);
		} catch (InvalidKeyException e) {
			log.debug("Error: "+e.getMessage());
		} catch (NoSuchAlgorithmException e) {
			log.debug("Error: "+e.getMessage());
		} catch (NoSuchPaddingException e) {
			log.debug("Error: "+e.getMessage());
		} catch (IllegalBlockSizeException e) {
			log.debug("Error: "+e.getMessage());
		} catch (BadPaddingException e) {
			log.debug("Error: "+e.getMessage());
		} catch (UnsupportedEncodingException e) {
			log.debug("Error: "+e.getMessage());
		} catch (InvalidAlgorithmParameterException e) {
			log.debug("Error: "+e.getMessage());
		}
		decoratedChannel.send(message);

	}

	private byte[]  encrypt(byte[]  message) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {

		// MODE is the encryption/decryption mode 
		// KEY is either a private, public or secret key 
		// IV is an init vector, needed for AES 

		crypt.init(Cipher.ENCRYPT_MODE,this.getSecretKey(),this.getIvParameter());

		byte [] plain = new byte[100]; // initialize to all zero bytes
		plain = message;

		// First encrypt: length of input (plain) is 100

		byte [] cipher = crypt.doFinal(plain);

		log.debug("length of cipher is " + cipher.length);

		return cipher;
	}

	@Override
	public byte[]  receive() {

		try {

			byte[] message = decoratedChannel.receive();

			return decrypt(message);

		} catch (InvalidKeyException e) {
			log.debug("Error: "+e.getMessage());
		} catch (IllegalBlockSizeException e) {
			log.debug("Error: "+e.getMessage());
		} catch (BadPaddingException e) {
			log.debug("Error: "+e.getMessage());
		} catch (UnsupportedEncodingException e) {
			log.debug("Error: "+e.getMessage());
		} catch (InvalidAlgorithmParameterException e) {
			log.debug("Error: "+e.getMessage());
		}
		return null;
	}

	private byte[]  decrypt(byte[]  message) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, UnsupportedEncodingException, InvalidAlgorithmParameterException {


		// Now decrypt: length of input(cipher) is 128;
		crypt.init(Cipher.DECRYPT_MODE,this.getSecretKey(),this.getIvParameter());

		if(message!=null){
			byte [] decrypted_cipher = crypt.doFinal(message);

			log.debug("length of decrypted cipher is " + decrypted_cipher.length);

			return decrypted_cipher;
		}
		return null;
	}

	@Override
	public PublicKey getPublicKey() {
		return null;
	}

	@Override
	public void setPublicKey(PublicKey publicKey) {

	}
	public void closeSocket(){
		decoratedChannel.closeSocket();
	}

	@Override
	public PrivateKey getPrivateKey() {
		return null;
	}

	@Override
	public void setPrivateKey(PrivateKey privateKey) {

	}

	public SecretKey getSecretKey() {
		return decoratedChannel.getSecretKey();
	}

	public void setSecretKey(SecretKey secretKey) {
		this.decoratedChannel.setSecretKey(secretKey);
	}

	public IvParameterSpec getIvParameter() {
		return decoratedChannel.getIvParameter();
	}

	public void setIvParameter(IvParameterSpec ivParameter) {
		this.decoratedChannel.setIvParameter(ivParameter);
	}

}
